#ifndef __reaction_enumerator_state__
#define __reaction_enumerator_state__

#include "molecule/molecule.h"
#include "reaction/reaction.h"
#include "graph/embedding_enumerator.h"
#include "base_cpp/reusable_obj_array.h"
#include "base_cpp/red_black.h"

class ReactionEnumeratorState
{
public:
   DEF_ERROR("Reaction product enumerator state");

   class ReactionMonomers
   {
   public:
      DEF_ERROR("Reaction product enumerator");

      TL_CP_DECL(ReusableObjArray<Molecule>, _monomers);
      TL_CP_DECL(Array<int>, _reactant_indexes);
      TL_CP_DECL(Array<int>, _deep_levels);
      TL_CP_DECL(Array<int>, _tube_indexes);

      ReactionMonomers();

      int size();

      const Molecule & getMonomer( int reactant_idx, int index );

      const Molecule & getMonomer( int mon_index );

      void addMonomer( int reactant_idx, const Molecule &monomer, int deep_level = 0, int tube_idx = -1 );

      void removeMonomer( int idx );
   };
   
   void (*product_proc)( const Molecule &product, Array<int> &monomers_indices, void *userdata );
   void *userdata;
   bool is_all_emb;
   bool is_self_react;
   bool is_one_tube;
   int max_deep_level;
   int max_product_count;

   int buildProduct( void );

   ReactionEnumeratorState( const Reaction &cur_reaction, Molecule &cur_full_product, 
      Array<int> &cur_product_aam_array, RedBlackStringMap<int> &cur_smiles_array, 
      ReactionMonomers &cur_reaction_monomers, int &cur_product_coint, 
      ObjArray< Array<int> > &cur_tubes_monomers );

   ReactionEnumeratorState( ReactionEnumeratorState &cur_rpe_state );

private:
   const Reaction &_reaction;
   int &_product_count;
   int _reactant_idx;
   TL_CP_DECL(Array<int>, _fragments_aam_array);
   TL_CP_DECL(Molecule, _full_product);
   TL_CP_DECL(Array<int>, _product_monomers);
   TL_CP_DECL(Array<byte>, _is_used_hydrogens);
   TL_CP_DECL(Array<int>, _product_aam_array);
   TL_CP_DECL(Molecule, _fragments);
   TL_CP_DECL(Array<byte>, _is_needless_atom);
   TL_CP_DECL(ObjArray< Array<int> >, _att_points);
   RedBlackStringMap<int> &_smiles_array;
   ReactionMonomers &_reaction_monomers;
   ObjArray< Array<int> > &_tubes_monomers;
   int _deep_level;
   bool _is_frag_search;
   
   static void _foldHydrogens( Molecule &molecule );

   void _productProcess( void );

   bool _nextMatchProcess( EmbeddingEnumerator &ee, const Molecule &monomer );

   void _markAromaticBonds( Molecule &molecule );

   void _start_ee( const Molecule &monomer );

   void _changeQueryNode( Molecule &ee_reactant, int change_atom_idx, int is_double );

   void _findFragAtoms( Array<byte> &unfrag_mon_atoms, const Molecule &submolecule, int *core_sub, int *core_super );

   bool _isFragmentCorrect( const MoleculeRGroups *sub_rgroups, 
      const MoleculeDecomposer &frag_decomp, const Array<int> &sub_qa_array, 
      const int *mapping );

   int _findAttachmentPoint( int idx );

   int _findRGroupsReactant( int rg_idx );

   void _cleanFragments( void );

   void _findR2PMapping( const Molecule &reactant, const  Molecule &product,
      Array<int> &mapping);

   void _invertStereocenters( Molecule &molecule, int edge_idx );

   void _cistransUpdate( const Molecule &submolecule, Molecule &supermolecule, 
      int *frag_mapping, const Array<int> &rp_mapping, int *core_sub);

   void _stereocentersUpdate( const Molecule &submolecule,
      const Molecule &supermolecule, const Array<int> &rp_mapping,
      int *core_sub, int *core_super );

   void _anyReplace( Molecule &product, Molecule &uncleaned_fragments );

   void _completeCisTranse( Molecule &product, Molecule &uncleaned_fragments, 
                            Array<int> &frags_mapping );

   bool _attachFragments( Molecule &product );

   void _addFragments( const Molecule &fragment, const Molecule &submolecule, 
      const Array<int> &sub_rg_atoms, int *core_sub, int *core_super );

   static bool _matchVertexCallback( const Graph &subgraph, const Graph &supergraph,
      const int *core_sub, int sub_idx, int super_idx, void *userdata );

   static bool _matchEdgeCallback( const Graph &subgraph, const Graph &supergraph,
      int self_idx, int other_idx, void *userdata );

   static int _embeddingCallback( const Graph &subgraph, const Graph &supergraph,
      int *core_sub, int *core_super, void *userdata );
};


#endif /* __reaction_enumerator_state__ */